from pml.Parser.public import *

class BinaryOperator(Node):
    def __init__(self, left=None, op=None, right=None, bp=None, ep=None, depth=0):
        super().__init__('binary', bp, ep, depth)
        self.left = left
        self.op = op
        self.right = right
    def __repr__(self):
        depth = '\t' * self.depth
        return 'Binary({})(\n{}left={},\n{}op={},\n{}right={}\n{})'.format(self.depth, depth, self.left, depth, self.op, depth, self.right, "\t" * (self.depth - 1))

    def getObject(self):
        self.methods['__init__'] = self.tp_init

        return self

    @staticmethod
    def tp_init(frame, this, position):
        pass

class UnaryOperator(Node):
    def __init__(self, op=None, right=None, left=True, bp=None, ep=None, depth=0):
        super().__init__('unary', bp, ep, depth)
        self.op = op
        self.right = right
        self.left = left
    def __repr__(self):
        depth = '\t' * self.depth
        return 'Unary({})(\n{}op={},\n{}right={},\n{}left={}\n{})'.format(self.depth, depth, self.left, depth, self.op, depth, self.right, "\t" * (self.depth - 1))

    def getObject(self):
        self.methods['__init__'] = self.tp_init

        return self

    @staticmethod
    def tp_init(frame, this, position):
        pass

class Member(Node):
    def __init__(self, father=None, member=None, bp=None, ep=None, depth=0):
        super().__init__('member', bp, ep, depth)
        self.father = father
        self.member = member

    def __repr__(self):
        depth = '\t' * self.depth
        return 'Member({})(\n{}father={},\n{}member={}\n{})'.format(self.depth, depth, self.father, depth, self.member, '\t' * (self.depth - 1))

    def getObject(self):
        self.methods['__init__'] = self.tp_init

        return self

    @staticmethod
    def tp_init(frame, this, position):
        pass

class Call(Node):
    def __init__(self, id=None, args=None, bp=None, ep=None, depth=0):
        super().__init__('call', bp, ep, depth)
        self.id = id
        self.args = args
    def __repr__(self):
        depth = '\t' * self.depth
        return 'Call({})(\n{}id={},\n{}args={}\n{})'.format(self.depth, depth, self.id, depth, self.args, "\t" * (self.depth - 1))

    def getObject(self):
        self.methods['__init__'] = self.tp_init
        self.methods['__print__'] = self.tp_print

        return self

    @staticmethod
    def tp_init(frame, this):
        pass

    @staticmethod
    def tp_print(frame, this, position):
        return this.tp_string(frame, this)

class Expression(Node):
    def __init__(self, expression=None, bp=None, ep=None, depth=0):
        super().__init__('expression', bp, ep, depth)
        self.expression = expression
    def __repr__(self):
        return 'Expression({})(\n{}expression={}\n{})'.format(self.depth, '\t' * self.depth, self.expression, '\t' * (self.depth - 1))

    def getObject(self):
        self.methods['__init__'] = self.tp_init

        return self

    @staticmethod
    def tp_init(frame, this, position):
        pass

class ExpressionBuilder(ASTBuilder):
    def __init__(self, tokens=None, priority=None, depth=0):
        super().__init__('Expression', tokens, depth)
        self.binaryOperator = [i.obj.value for i in TokenConstants.TH_BINARY_OPERATOR]
        self.priority = {
            TokenConstants.TV_SET_VALUE: 1,

            TokenConstants.TV_ADD: 3,
            TokenConstants.TV_SUB: 3,
            TokenConstants.TV_MUL: 4,
            TokenConstants.TV_DIV: 4,
            TokenConstants.TV_MOD: 4,

            TokenConstants.TV_POINT: 5
        } if priority is None else priority

    def getObject(self):
        self.methods['__init__'] = self.tp_init

        self.methods['buildExpression'] = self.buildExpression

        return self

    @staticmethod
    def buildExpression(frame, this: 'ExpressionBuilder', position):
        bp = this.token.bp
        left = this.buildExpressionNode(depth=this.depth + 1)
        ep = this.token.ep
        try:
            this.getNextToken()
        except StopIteration:
            return Objects.object.PMTypeObject(Expression(left, bp, ep, depth=this.depth).getObject())
        left.obj.depth += 1
        expression = this.expression(left, this.token.bp, depth=this.depth + 1)
        this.getLastToken()
        ep = this.token.ep
        try:
            this.getNextToken()
        except StopIteration:
            pass
        return Objects.object.PMTypeObject(Expression(expression, bp, ep, depth=this.depth).getObject())

    def expression(self, left, bp, depth):
        if self.token.type != TokenConstants.TT_OPERATION:
            return left
        operator = self.token.value
        self.getNextToken()
        try:
            right = self.buildExpressionNode(depth + 1)
        except EOFError as e:
            if operator.obj.value in [i.obj.value for i in TokenConstants.TH_RIGHT_UNARY_OPERATORS]:
                return Objects.object.PMTypeObject(UnaryOperator(operator, left, False, bp, self.token.ep, depth=depth).getObject())
            raise EOFError(e)
        ep = self.token.ep
        try:
            self.getNextToken()
            if self.token.type != TokenConstants.TT_OPERATION:
                if operator == TokenConstants.TV_POINT:
                    if right.obj.name == 'call':
                        left.obj.depth += 1
                        return Objects.object.PMTypeObject(Call(Objects.object.PMTypeObject(
                            Member(left, right.obj.id, bp, ep, depth=depth + 1).getObject()),
                                                                right.obj.args, left.obj.bp, right.obj.ep,
                                                                depth=depth).getObject())
                    return Objects.object.PMTypeObject(Member(left, right, bp, ep, depth=depth).getObject())
                return Objects.object.PMTypeObject(BinaryOperator(left, operator, right, bp, ep, depth=depth).getObject())
        except StopIteration:
            if operator == TokenConstants.TV_POINT:
                if right.obj.name == 'call':
                    left.obj.depth += 1
                    return Objects.object.PMTypeObject(Call(Objects.object.PMTypeObject(Member(left, right.obj.id, bp, ep, depth=depth + 1).getObject()),
                                                            right.obj.args, left.obj.bp, right.obj.ep, depth=depth).getObject())
                return Objects.object.PMTypeObject(Member(left, right, bp, ep, depth=depth).getObject())
            return Objects.object.PMTypeObject(BinaryOperator(left, operator, right, bp, ep, depth=depth).getObject())
        nextOperator = self.token.value
        if self.priority[operator] >= self.priority[nextOperator]:
            left.obj.depth += 1
            right.obj.depth += 1
            if operator == TokenConstants.TV_POINT:
                if right.obj.name == 'call':
                    left.obj.depth += 1
                    return self.expression(Objects.object.PMTypeObject(Call(Objects.object.PMTypeObject(Member(left, right.obj.id, bp, ep, depth=depth + 2).getObject()),
                                                            right.obj.args, left.obj.bp, right.obj.ep, depth=depth + 1).getObject()), ep, depth=depth)
                return self.expression(Objects.object.PMTypeObject(Member(left, right, bp, ep, depth=depth + 1).getObject()), ep, depth=depth)
            return self.expression(Objects.object.PMTypeObject(BinaryOperator(left, operator, right, bp, ep, depth=depth + 1).getObject()), ep, depth=depth)
        else:
            if operator == TokenConstants.TV_POINT:
                return Objects.object.PMTypeObject(Member(left, self.expression(right, ep, depth=depth + 1), bp, ep, depth=depth).getObject())
            return Objects.object.PMTypeObject(BinaryOperator(left, operator, self.expression(right, ep, depth=depth + 1), bp, ep, depth=depth).getObject())

    def buildExpressionNode(self, depth):
        token = self.token
        if token.type.obj.value in [i.obj.value for i in TokenConstants.TH_NUMBERTYPES]:
            return Objects.object.PMTypeObject(Number(token.type, token.value, token.bp, token.ep).getObject())
        if token.type == TokenConstants.TT_NAME:
            bp = token.bp
            name = Objects.object.PMTypeObject(Name(token.value, bp, token.ep).getObject())
            try:
                self.getNextToken()
            except StopIteration:
                self.getLastToken()
                return name
            if self.token.type != TokenConstants.TT_SIGN or self.token.value != TokenConstants.TV_LP1:
                self.getLastToken()
                return name
            args = []
            self.getNextToken()
            while self.token.type != TokenConstants.TT_SIGN and self.token.value != TokenConstants.TV_RP1:
                if self.token.type != TokenConstants.TT_NAME:
                    # error
                    pass
                expressionBuilder = Parser.Expression.ExpressionBuilder(self.tokens, depth=self.depth + 2)
                args.append(expressionBuilder.buildExpression(None, expressionBuilder, None))
                self.updateToken()
                if self.token.type == TokenConstants.TT_SIGN and self.token.value == TokenConstants.TV_RP1:
                    break
                if self.token.type != TokenConstants.TT_SIGN or self.token.value != TokenConstants.TV_COMMA:
                    # error
                    pass
                self.getNextToken()
            return Objects.object.PMTypeObject(Call(name, args, bp, self.token.ep, depth=depth).getObject())

        if token.type == TokenConstants.TT_OPERATION and token.value in TokenConstants.TH_LEFT_UNARY_OPERATORS:
            bp = token.bp
            operator = token.value
            self.getNextToken()
            value = self.buildExpressionNode(depth=depth + 1)
            return Objects.object.PMTypeObject(UnaryOperator(operator, value, True, bp, self.token.ep, depth=depth).getObject())

        raise EOFError(token)

    @staticmethod
    def tp_init(frame, this, position):
        pass
